#include "head.h"

/**
 * 向通讯录中添加联系人
 * @param head 通讯录头指针的指针，用于修改头指针
 * @param count 联系人计数指针，用于分配唯一ID
 */
void add_contact(AddressBook **head, int *count) {
    // 分配内存用于存储新联系人
    AddressBook *contact = (AddressBook *)malloc(sizeof(AddressBook));
    if (contact == NULL) {
        printf("内存分配失败！\n");
        return;
    }
    // 设置联系人ID为当前计数加1
    contact->id = ++(*count);
    printf("请输入姓名：");
    scanf("%19s", contact->name);
    // 获取并设置联系人性别
    int sex_input;
    printf("请输入性别（0 表示男，1 表示女）：");
    scanf("%d", &sex_input);
    contact->sex = (sex_input == 1) ? WOMAN : MAN;
    // 获取并设置联系人电话号码
    printf("请输入电话号码：");
    scanf("%11s", contact->phone);
    contact->next = NULL;

    // 如果通讯录为空，将新联系人设为头节点
    if (*head == NULL) {
        *head = contact;
    } else {
        // 否则遍历到链表末尾，添加新联系人
        AddressBook *p = *head;
        while (p->next != NULL) p = p->next;
        p->next = contact;
    }
    // 添加成功后显示联系人信息
    printf("添加成功！\n");
    printf("姓名：%s\n", contact->name);
    printf("性别：%s\n", (contact->sex == MAN) ? "男" : "女");
    printf("电话：%s\n", contact->phone);
}

/**
 * 在通讯录中查找指定姓名的联系人
 * @param head 通讯录链表的头指针
 */
void search_contact(const AddressBook *head) {
    char name[20];  // 用于存储用户输入的查找姓名
    // 提示用户输入要查找的姓名
    printf("请输入要查找的姓名：");
    scanf("%19s", name);  // 限制输入长度为19个字符，防止缓冲区溢出
    // 从头开始遍历通讯录链表
    const AddressBook *p = head;
    while (p != NULL) {
        // 比较当前联系人姓名与输入的姓名
        if (strcmp(p->name, name) == 0) {
            // 找到匹配的联系人，输出其信息
            printf("姓名：%s\n", p->name);
            printf("性别：%s\n", (p->sex == MAN) ? "男" : "女");  // 使用三元运算符判断性别
            printf("电话：%s\n", p->phone);
            return;  // 找到后立即返回
        }
        p = p->next;  // 移动到下一个联系人
    }
    // 遍历完整个链表后仍未找到匹配的联系人
    printf("没有找到该联系人！\n");
}

/**
 * 修改联系人信息的函数
 * @param head 通讯录链表的头指针
 */
void modify_contact(AddressBook *head) {
    char name[20];  // 用于存储用户输入的要修改的联系人姓名
    // 提示用户输入要修改的联系人姓名
    printf("请输入要修改的姓名：");
    scanf("%19s", name);
    AddressBook *p = head;  // 创建一个指针p，初始化为头指针
    // 遍历通讯录链表，查找匹配的联系人
    while (p != NULL) {
        // 比较当前联系人姓名与输入的姓名是否相同
        if (strcmp(p->name, name) == 0) {
            // 找到匹配联系人，提示用户输入新信息
            // 修改姓名
            printf("请输入新姓名：");
            scanf("%19s", p->name);

            
            // 修改性别
            int sex_input;  // 用于存储用户输入的性别值
            printf("请输入新性别（0 表示男，1 表示女）：");
            scanf("%d", &sex_input);
            p->sex = (sex_input == 1) ? WOMAN : MAN;  // 根据输入设置性别
            // 修改电话号码
            printf("请输入新电话号码：");
            scanf("%11s", p->phone);
            printf("修改成功！\n");  // 提示修改成功
            return;  // 结束函数
        }
        p = p->next;  // 移动到下一个联系人
    }
    // 遍历完整个链表后未找到匹配联系人
    printf("没有找到该联系人！\n");
}

/**
 * 删除联系人的函数
 * @param head 通讯录链表的头指针的指针
 * @param count 通讯录中联系人数量的指针
 */
void delete_contact(AddressBook **head, int *count) {
    char name[20];  // 用于存储用户输入的要删除的联系人姓名
    // 提示用户输入要删除的联系人姓名
    printf("请输入要删除的姓名：");
    scanf("%19s", name);
    AddressBook *p = *head;  // 当前遍历的节点指针
    AddressBook *q = NULL;   // 前一个节点的指针
    // 遍历通讯录链表
    while (p != NULL) {
        // 找到姓名匹配的联系人
        if (strcmp(p->name, name) == 0) {
            // 如果要删除的是头节点
            if (q == NULL) {
                *head = p->next;  // 更新头指针为下一个节点
            } else {
                q->next = p->next;  // 跳过当前节点
            }
            free(p);  // 释放当前节点的内存
            (*count)--;  // 联系人数量减一
            printf("删除成功！\n");
            return;  // 删除成功后直接返回
        }
        // 更新前一个节点和当前节点
        q = p;
        p = p->next;
    }
    // 遍历完整个链表后仍未找到匹配的联系人
    printf("没有找到该联系人！\n");
}

/**
 * 在通讯录链表中插入一个新的联系人
 * @param head 通讯录链表的头指针的指针
 * @param count 当前通讯录中联系人的数量
 */
void insert_contact(AddressBook **head, int *count) {
    int position;  // 用于存储用户想要插入的位置
    // 提示用户输入插入位置
    printf("请输入插入位置 (1 为头部)：");
    scanf("%d", &position);
    // 检查位置是否有效
    if (position < 1) {
        printf("位置无效！\n");
        return;
    }
    // 为新联系人分配内存
    AddressBook *new_contact = (AddressBook *)malloc(sizeof(AddressBook));
    if (new_contact == NULL) {
        printf("内存分配失败！\n");
        return;
    }
    // 设置新联系人的ID
    new_contact->id = ++(*count);
    // 获取用户输入的联系人信息
    printf("请输入姓名：");
    scanf("%19s", new_contact->name);
    int sex_input;  // 用于存储用户输入的性别
    printf("请输入性别（0 表示男，1 表示女）：");
    scanf("%d", &sex_input);
    new_contact->sex = (sex_input == 1) ? WOMAN : MAN;
    printf("请输入电话号码：");
    scanf("%11s", new_contact->phone);

    // 根据位置插入联系人
    if (position == 1) {
        // 插入到链表头部
        new_contact->next = *head;
        *head = new_contact;
    } else {
        // 插入到链表中间或尾部
        AddressBook *p = *head;
        int i = 1;
        // 遍历链表找到插入位置的前一个节点
        while (p != NULL && i < position - 1) {
            p = p->next;
            i++;
        }
        // 检查位置是否超出链表长度
        if (p == NULL) {
            printf("位置超出链表长度，插入到尾部。\n");
            free(new_contact);
            return;
        }
        // 插入节点
        new_contact->next = p->next;
        p->next = new_contact;
    }
    printf("插入成功！\n");
}

/**
 * 显示通讯录中所有联系人的信息
 * @param head 通讯录链表的头指针
 */
void show_all_contacts(const AddressBook *head) {
    // 检查通讯录是否为空
    if (head == NULL) {
        printf("通讯录为空！\n");
        return;
    }
    // 创建一个指针p用于遍历链表，从头节点开始
    const AddressBook *p = head;
    // 遍历链表，直到指针p为NULL（即到达链表末尾）
    while (p != NULL) {
        // 打印当前联系人的ID、姓名、性别和电话信息
        // 性别使用三元运算符判断，MAN为"男"，否则为"女"
        printf("ID: %d, 姓名：%s, 性别：%s, 电话：%s\n",
               p->id, p->name, (p->sex == MAN) ? "男" : "女", p->phone);
        // 移动指针到下一个节点
        p = p->next;
    }
}

/**
 * 将通讯录数据导出到文件中
 * @param head 通讯录链表的头指针
 */
void export_contacts(const AddressBook *head) {
    // 以写入模式打开文件 "contacts.txt"
    FILE *fp = fopen("contacts.txt", "w");
    if (fp == NULL) {
        // 如果文件打开失败，输出错误信息并返回
        printf("文件打开失败！\n");
        return;
    }
    // 创建一个指针p，初始指向链表头
    const AddressBook *p = head;
    // 遍历链表，直到指针p为NULL
    while (p != NULL) {
        // 将当前节点的信息写入文件，格式为：id,name,sex,phone
        fprintf(fp, "%d,%s,%d,%s\n", p->id, p->name, p->sex, p->phone);
        // 移动到下一个节点
        p = p->next;
    }
    // 关闭文件
    fclose(fp);
    // 输出导出成功的提示信息
    printf("导出成功！\n");
}

/**
 * @brief 对通讯录按姓名进行排序的函数
 * @param head 通讯录链表的头指针
 * @note 使用冒泡排序算法，按姓名升序排列
 */
void sort_contacts(AddressBook *head) {
    // 如果链表为空，直接返回
    if (head == NULL) return;
    AddressBook *p = head;
    // 外层循环，遍历链表中的每个节点
    while (p != NULL) {
        AddressBook *q = p->next;
        // 内层循环，将当前节点与后面的所有节点比较
        while (q != NULL) {
            // 如果当前节点的姓名大于后面节点的姓名，则交换数据
            if (strcmp(p->name, q->name) > 0) {
                // 交换姓名
                char temp_name[20];
                strcpy(temp_name, p->name);
                strcpy(p->name, q->name);
                strcpy(q->name, temp_name);
                // 交换性别
                SEX temp_sex = p->sex;
                p->sex = q->sex;
                q->sex = temp_sex;
                // 交换电话号码
                char temp_phone[12];
                strcpy(temp_phone, p->phone);
                strcpy(p->phone, q->phone);
                strcpy(q->phone, temp_phone);
                // 交换ID
                int temp_id = p->id;
                p->id = q->id;
                q->id = temp_id;
            }
            q = q->next; // 移动到下一个节点
        }
        p = p->next; // 移动到下一个节点
    }
    printf("排序成功！\n"); // 输出排序完成提示
}

/**
 * 释放通讯录中所有联系人的内存
 * @param head 指向通讯录头指针的指针
 */
void free_all_contacts(AddressBook **head) {
    AddressBook *p = *head;  // 创建一个指针p指向链表的头节点
    while (p != NULL) {      // 遍历链表，直到p为NULL（链表末尾）
        AddressBook *temp = p;  // 创建临时指针temp指向当前节点
        p = p->next;           // 将p移动到下一个节点
        free(temp);            // 释放当前节点的内存
    }
    *head = NULL;             // 将头指针设置为NULL，防止悬空指针
}